
//! enum of entity types
enum EEntityType
{
	eEntityTypeInvalid			= 0, // invalid or not initialized
	eEntityTypePlayer			= 1,
	eEntityTypeBox				= 2,
	eEntityTypeSphere			= 3,
	eEntityTypeCylinder			= 4,
	eEntityTypeZone				= 5,
	eEntityTypeCollision		= 6,
	eEntityTypeConstraintHinge	= 7,
	eEntityTypeConstraintD6		= 8,
	eEntityTypeEarth			= 9,
	eEntityTypeCometFire		= 10,
	eEntityTypeCometIce			= 11,
	eEntityTypeAsteroidRed		= 12,
	eEntityTypeAsteroidBlue		= 13,

//	eEntityTypeCapsule			= 4,
//	eEntityTypeCone				= 5,
//	eEntityTypeStaticPlane		= 6,
//	eEntityTypeConvexMesh		= 7,
//	eEntityTypeConcaveMesh		= 8,

	eMaxEntityType // must be last
};

// forward declare
class EntConstraintData;
// base class of all entities in game
class EntBase
{
public:
	EntBase( VxIrrBase * poIrrBase );
	virtual ~EntBase();

	//=== properties ===//
	irr::core::stringc&	GetCommonName()						{ return m_strEntCommonName; }
	void				SetCommonName( const char * pName )	{ m_strEntCommonName = pName; }
	irr::core::stringc&	GetProperName() 					{ return m_strEntProperName; }
	void				SetProperName( const char * pName )	{ m_strEntProperName = pName; }

	S32					GetEntId() const					{ return m_s32EntId; }
	void				SetEntId( S32 s32Id )				{ m_s32EntId = s32Id; }
	S32					GetOwnerId() const					{ return m_s32OwnerId; }
	void				SetOwnerId( S32 s32OwnerId )		{ m_s32OwnerId = s32OwnerId; }
	S32					GetTemplateID() const				{ return m_s32TemplateId; }
	void				SetTemplateId(S32 s32TemplateId)	{ m_s32TemplateId = s32TemplateId; }
	int					GetEntType() const					{ return m_eEntType; }
	void				SetEntType( EEntityType eType )		{ m_eEntType = eType; }

	BOOL				GetDeleted() const					{ return m_bDeleted; }
	void				SetDeleted( BOOL Value)				{ m_bDeleted = Value; }
	void				SetLifetime( S32 s32LifeTimeMs )	{ m_s32LifeTimeMs = m_s32EntAliveTimer + s32LifeTimeMs; }
	S32					GetLifetime() const					{ return m_s32LifeTimeMs; }

	//! set power up level
	void				SetPowerLevel( S32 s32PowerLevel )	{ m_s32PowerLevel = s32PowerLevel; }
	//! get power up level
	S32					GetPowerLevel() const				{ return m_s32PowerLevel; }
	//! set time in seconds that power up lasts
	void				SetPowerUpTTL( S32 s32TTL )			{ m_s32PowerUpTTL = s32TTL; }
	//! get time in seconds that power up has to live
	S32					GetPowerUpTTL() const				{ return m_s32PowerUpTTL; }


	btRigidBody *				GetPhysBody( void )				{ return m_poRigidBody; }
	irr::scene::ISceneNode *	GetSceneNode( void )			{ return m_poSceneNode; }
	BOOL						IsTouchingGround( void ) const	{ return m_bTouchingGround; }
	BOOL						IsTouchingWall( void ) const	{ return m_bTouchingWall; }

	//=== methods ===//
	//! return true is scene node is this entities or any children like particle system etc
	BOOL				IsMySceneNode( irr::scene::ISceneNode * poNode );


	//=== position and movement methods ===//
	//! reset position of entity to starting position
	void				ResetToStartingPosition( void );
	//! set starting position of entity		
	void				SetStartPosition( const irr::core::vector3df& vec3Position ) { m_v3fStartPosition = vec3Position; }
	//! set starting rotation of entity		
	void				SetStartRotation( const irr::core::vector3df& vec3Rotation) { m_v3fStartRotation = vec3Rotation; }

	//! set position of last collision	
	void				SetLastCollisionPosition( const irr::core::vector3df& v3fPos );
	//! get position of last collision	
	irr::core::vector3df& GetLastCollisionPosition( void );

	//=== irrlicht only ===//
	//! get irrlicht position of entity
	inline irr::core::vector3df	GetIrrPosition( void )						{ return m_poSceneNode->getPosition(); }
	//! set irrlicht position of entity
	inline void			SetIrrPosition( const irr::core::vector3df& v3fPos ){ m_poSceneNode->setPosition( v3fPos ); }
	//! get irrlicht position of entity
	inline irr::core::vector3df	GetIrrRotation( void )						{ return m_poSceneNode->getRotation(); }
	//! set irrlicht position of entity
	inline void			SetIrrRotation( const irr::core::vector3df& v3fPos ){ m_poSceneNode->setRotation( v3fPos ); }

	//=== physics only ===//

	//! get physics position of entity in bullet coords
	const btVector3&	GetPhysicsPosition() const							{ return m_poRigidBody->getWorldTransform().getOrigin(); }
	//! set position of entity
	void				SetPhysicsPosition( const btVector3& vec3Position );
	//! set physics position of entity using irrlicht coords
	void				SetPhysicsPosition( const irr::core::vector3df& v3fPosition );
	//! set physics position of entity using floats
	void				SetPhysicsPosition( irr::f32 f32PosX, irr::f32 f32PosY, irr::f32 f32PosZ );

	//! get rotation of physics shape in bullet coords 
	btQuaternion		GetPhysicsRotation( void )							{ return m_poRigidBody->getWorldTransform().getRotation(); }
	//! set rotation of physics shape using bullet Quat
	void				SetPhysicsRotation(const btQuaternion &vec3Rotation)					{ m_poRigidBody->getWorldTransform().setRotation(vec3Rotation); }
	//! convert irr coord to bullet quat then set rotation of physics shape using bullet Quat
	void				SetPhysicsRotation(const irr::core::vector3df& v3fRot)					{ m_poRigidBody->getWorldTransform().setRotation(btQuaternion( v3fRot.Y * irr::core::DEGTORAD, v3fRot.X * irr::core::DEGTORAD, v3fRot.Z * irr::core::DEGTORAD)); }
		//! convert irr coord to bullet quat then set rotation of physics shape using bullet Quat
	void				SetPhysicsRotation(irr::f32 f32RotX, irr::f32 f32RotY, irr::f32 f32RotZ)	{ m_poRigidBody->getWorldTransform().setRotation(btQuaternion( f32RotY * irr::core::DEGTORAD, f32RotX * irr::core::DEGTORAD, f32RotZ * irr::core::DEGTORAD)); }

	//! get rotation of physics shape in irrlicht coords ( degrees )
	void				GetPhysicsRotationInDegrees( irr::core::vector3df& v3fRetRot );

	//=== irrlich plus physics ===//
	//! set physics and graphical position of entity using irrlicht coords		
	void				SetIrrPlusPhysicsPosition( const irr::core::vector3df& v3fPos );
	//! set physics and graphical position and rotation of entity using irrlicht coords		
	void				SetIrrPlusPhysicsPositionAndRotation( const irr::core::vector3df& v3fPos, const irr::core::vector3df& v3fRot );

	//=== physics forces ===//
	//! set speed and heading of entity movement
	void				SetLinearVelocity(const btVector3 &vec3Velocity);
	//! get speed and heading of entity movement
	const btVector3&	GetLinearVelocity( void )							{ return m_poRigidBody->getLinearVelocity(); }
	//! set speed and angle of entity rotation
	void				SetAngularVelocity(const btVector3& vec3Velocity)	{ m_poRigidBody->setAngularVelocity(vec3Velocity); }
	//! get speed and angle of entity rotation
	const btVector3&	GetAngularVelocity( void )							{ return m_poRigidBody->getAngularVelocity(); }
	//! apply force with direction and rotational vectors
	void				ApplyForce(const btVector3& vec3Force)				{ m_poRigidBody->applyForce(vec3Force,  btVector3(0.0f,0.0f,0.0f)); }

	//! halt physics body movement
	virtual void		StopMovement( void );

	//=== create entity methods in EntBaseCreate ===//
	//! create entity from spawn data
	virtual	RCODE		CreateEntity( const EntSpawnData& oEntSpawnData, irr::f32 f32Scale = 1.0f );

		//! interporlate between last and current positions
	void				CalculateInterpolatedPosition( float f32BlendFactor );

	//=== update and render ===//
	//! process before frame is rendered 
	virtual void		BeginFrame( void );
	//! updates the position and rotation for graphical render of the entity
	virtual void		UpdateRender( void );
	//! update
	virtual void		Update( U32 u32FrameTimeMs );
	//! process after frame is rendered 
	virtual void		EndFrame( void );

	//! hande collision with another object
	//! return non zero if caller should not process any more contact points
	virtual RCODE		HandleCollision( EntBase * poOtherEntity, const btPersistentManifold * poContactManifold, float f32NormalScale );
	//! called after hande collision has determined if touching ground or wall
	//! return 0 if scripted collistion should be called else return error code
	virtual RCODE		OnCollision( EntBase * poOtherEntity, const btPersistentManifold * poContactManifold, float f32NormalScale );

	//! called once per second
	virtual void		OncePerSecond( void ){};
	//! handle changes when power level goes up
	virtual void		OnPowerLevelUp( S32 s32PowerLevel, S32 s32PowerUpTTL ){};
	//! handle changes when power level goes down
	virtual void		OnPowerLevelDown( void ){};
private:

	//! create entity helper.. create mesh from template data
	virtual	RCODE		CreateMesh( EntTemplate * poEntTemplate, irr::f32 f32Scale );
	//------------------------------------------------------------------------------
	//! load and apply textures from template
	virtual RCODE		ApplyTextures( EntTemplate * poEntTemplate );
	//! create entity helper.. create billboard from template data
	virtual RCODE		CreateBillboard( EntTemplate * poEntTemplate, irr::f32 f32Scale );
	//! create entity helper.. create physics shape.. you should override if not box
	virtual	btCollisionShape * CreatePhysicsShape( EntTemplate * poEntTemplate, irr::f32 f32Scale );
	//! create entity helper.. create a physics body object and add it to the world
	virtual	void		CreatePhysicsBody( const EntSpawnData& oEntSpawnData, btCollisionShape * poPhysShape ) ;
	//! create Motion State.. give derived classes a chance to override
	virtual	IrrPhysMotionState *	CreateMotionState( irr::scene::ISceneNode *	poSceneNode );


	//! set entity properties from spawn data
	virtual	void		SetEntProperties(const EntSpawnData& oEntSpawnData );
	//! set entity constraint properties
	virtual	void		SetEntProperties(const EntConstraintData * poEntConstraintData );


public:
	//=== vars ===//
	VxIrrBase *					m_poIrrBase;				// irrlicht base class
	//=== identity vars ===//
	irr::core::stringc			m_strEntProperName;			// entity proper name ( ie Rover )
	irr::core::stringc			m_strEntCommonName;			// entity common name ( ie dog )
	S32							m_s32EntId;					// id of this entity
	S32							m_s32OwnerId;				// id of owner of this entity
	S32							m_s32TemplateId;			// id of entity template
	int							m_eEntType;					// type of entity

	//=== collision vars ===//
	BOOL						m_bTouchingGround;				// object is touching ground
	BOOL						m_bTouchingWall;				// object is touching wall
	irr::core::stringc			m_strScriptCollisionCallback;	// call script function on collision
	irr::core::vector3df		m_v3fLastCollisionPosition;		// last position when entity collided
	U32							m_u32LastCollisionTimeMs;		// time of last collision

	//=== Physics vars ===//
	btRigidBody *				m_poRigidBody;				// physics body
	btVector3					m_vec3LastPosition;			// last postition of entity
	btVector3					m_vec3InterpolatedPosition;	// interpolated postition of entity
	btQuaternion				m_quatLastRotation;			// rotation as a quaternion
	IrrPhysMotionState *		m_poMotionState;

	//=== graphics vars ===//
	irr::scene::ISceneNode *	m_poSceneNode;
	irr::scene::IAnimatedMesh *	m_poMesh;
	// problem with irrlicht getSceneNodeFromScreenCoordinatesBB is that it may return a particle system instead of parent node
	// so we keep list of child nodes to compare to to see if the touch was really on us
	irr::core::array< irr::scene::ISceneNode *> m_aoChildNodes;

	//=== state vars ===//
	BOOL						m_bDeleted;					// has been deleted or about to be deleted
	S32							m_s32EntAliveTimer;			// time in millisec since entity was created
	S32							m_s32LifeTimeMs;			// if not zero then time in ms entity should live

	irr::core::vector3df		m_v3fStartPosition;			// starting point position
	irr::core::vector3df		m_v3fStartRotation;			// starting point rotation
	int							m_iTimeInStartPos;			// time in seconds entity has been in start position

	S32							m_s32PowerLevel;			// power up level
	S32							m_s32PowerUpTTL;			// time power up has to live in seconds
};

class EntConstraintData 
{
public:
	//=== vars ===//
	irr::core::stringc		m_strConstraintName;
	EntBase *				m_poEntA;
	EntBase *				m_poEntB;
	EntTemplate *			m_poEntTemplate;

	//=== constructor ===//
	EntConstraintData();
};

